﻿using System;
using System.Collections.Generic;
using System.Text;

namespace SupervisedSpeakerRecognition
{
    /// <summary>
    /// Class for complex numbers representation with all operators
    /// </summary>
    public class Complex
    {
        //************************  Private Fields  *******************************//
        private double x, y;
        //************************  Constructors    *******************************// 
        /**
        Constructs the complex number z = u + i*v
        @param u Real part
        @param v Imaginary part
        */
        public Complex(double u, double v)
        {
            x = u;
            y = v;
        }
        //************************  Public Methods  ****************************// 

        /**
            Real part of this Complex number 
            (the x-coordinate in rectangular coordinates).
            @return Re[z] where z is this Complex number.
        */
        public double real()
        {
            return x;
        }

        /**
            Imaginary part of this Complex number 
            (the y-coordinate in rectangular coordinates).
            @return Im[z] where z is this Complex number.
        */
        public double imag()
        {
            return y;
        }

        /**
            Modulus of this Complex number
            (the distance from the origin in polar coordinates).
            @return |z| where z is this Complex number.
        */
        public double mod()
        {
            if (x != 0 || y != 0)
            {
                return Math.Sqrt(x * x + y * y);
            }
            else
            {
                return 0d;
            }
        }

        /**
            Argument of this Complex number 
            (the angle in radians with the x-axis in polar coordinates).
            @return arg(z) where z is this Complex number.
        */
        public double arg()
        {
            return Math.Atan2(y, x);
        }

        /**
            Complex conjugate of this Complex number
            (the conjugate of x+i*y is x-i*y).
            @return z-bar where z is this Complex number.
        */
        public Complex conjugate()
        {
            return new Complex(x, -y);
        }

        /**
            Addition of Complex numbers (doesn't change this Complex number).
            <br>(x+i*y) + (s+i*t) = (x+s)+i*(y+t).
            @param w is the number to add.
            @return z+w where z is this Complex number.
        */
        public Complex plus(Complex w)
        {
            return new Complex(x + w.real(), y + w.imag());
        }

        /**
            Subtraction of Complex numbers (doesn't change this Complex number).
            <br>(x+i*y) - (s+i*t) = (x-s)+i*(y-t).
            @param w is the number to subtract.
            @return z-w where z is this Complex number.
        */
        public Complex minus(Complex w)
        {
            return new Complex(x - w.real(), y - w.imag());
        }

        /**
            Complex multiplication (doesn't change this Complex number).
            @param w is the number to multiply by.
            @return z*w where z is this Complex number.
        */
        public Complex times(Complex w)
        {
            return new Complex(x * w.real() - y * w.imag(), x * w.imag() + y * w.real());
        }

        /**
            Division of Complex numbers (doesn't change this Complex number).
            <br>(x+i*y)/(s+i*t) = ((x*s+y*t) + i*(y*s-y*t)) / (s^2+t^2)
            @param w is the number to divide by
            @return new Complex number z/w where z is this Complex number  
        */
        public Complex div(Complex w)
        {
            double den = Math.Pow(w.mod(), 2);
            return new Complex((x * w.real() + y * w.imag()) / den, (y * w.real() - x * w.imag()) / den);
        }

        /**
            Complex exponential (doesn't change this Complex number).
            @return exp(z) where z is this Complex number.
        */
        public Complex exp()
        {
            return new Complex(Math.Exp(x) * Math.Cos(y), Math.Exp(x) * Math.Sin(y));
        }

        /**
            Principal branch of the Complex logarithm of this Complex number.
            (doesn't change this Complex number).
            The principal branch is the branch with -pi < arg <= pi.
            @return log(z) where z is this Complex number.
        */
        public Complex log()
        {
            return new Complex(Math.Log(this.mod()), this.arg());
        }

        /**
            Complex square root (doesn't change this complex number).
            Computes the principal branch of the square root, which 
            is the value with 0 <= arg < pi.
            @return sqrt(z) where z is this Complex number.
        */
        public Complex sqrt()
        {
            double r = Math.Sqrt(this.mod());
            double theta = this.arg() / 2;
            return new Complex(r * Math.Cos(theta), r * Math.Sin(theta));
        }

        // Real cosh function (used to compute complex trig functions)
        private double cosh(double theta)
        {
            return (Math.Exp(theta) + Math.Exp(-theta)) / 2;
        }

        // Real sinh function (used to compute complex trig functions)
        private double sinh(double theta)
        {
            return (Math.Exp(theta) - Math.Exp(-theta)) / 2;
        }

        /**
            Sine of this Complex number (doesn't change this Complex number).
            <br>sin(z) = (exp(i*z)-exp(-i*z))/(2*i).
            @return sin(z) where z is this Complex number.
        */
        public Complex sin()
        {
            return new Complex(cosh(y) * Math.Sin(x), sinh(y) * Math.Cos(x));
        }

        /**
            Cosine of this Complex number (doesn't change this Complex number).
            <br>cos(z) = (exp(i*z)+exp(-i*z))/ 2.
            @return cos(z) where z is this Complex number.
        */
        public Complex cos()
        {
            return new Complex(cosh(y) * Math.Cos(x), -sinh(y) * Math.Sin(x));
        }

        /**
            Hyperbolic sine of this Complex number 
            (doesn't change this Complex number).
            <br>sinh(z) = (exp(z)-exp(-z))/2.
            @return sinh(z) where z is this Complex number.
        */
        public Complex sinh()
        {
            return new Complex(sinh(x) * Math.Cos(y), cosh(x) * Math.Sin(y));
        }

        /**
            Hyperbolic cosine of this Complex number 
            (doesn't change this Complex number).
            <br>cosh(z) = (exp(z) + exp(-z)) / 2.
            @return cosh(z) where z is this Complex number.
        */
        public Complex cosh()
        {
            return new Complex(cosh(x) * Math.Cos(y), sinh(x) * Math.Sin(y));
        }

        /**
            Tangent of this Complex number (doesn't change this Complex number).
            <br>tan(z) = sin(z)/cos(z).
            @return tan(z) where z is this Complex number.
        */
        public Complex tan()
        {
            return (this.sin()).div(this.cos());
        }

        /**
            Negative of this complex number (chs stands for change sign). 
            This produces a new Complex number and doesn't change 
            this Complex number.
            <br>-(x+i*y) = -x-i*y.
            @return -z where z is this Complex number.
        */
        public Complex chs()
        {
            return new Complex(-x, -y);
        }

        /**
            String representation of this Complex number.
            @return x+i*y, x-i*y, x, or i*y as appropriate.
        */
        public String toString()
        {
            if (x != 0 && y > 0)
            {
                return x + " + " + y + "i";
            }
            if (x != 0 && y < 0)
            {
                return x + " - " + (-y) + "i";
            }
            if (y == 0)
            {
                return x.ToString();
            }
            if (x == 0)
            {
                return y + "i";
            }
            // shouldn't get here (unless Inf or NaN)
            return x + " + i*" + y;

        }
        public Complex times(double d)
        {
            return new Complex(x * d, y * d);
        }

    }
}
